///////////////////////////////////////////////////////////////////////////////
// The Archimeds project 
//
// C++ Implementation: mtmlogger
//
// Description: 
//
//
// Author: Archimedes Projekt <thelsb@gmail.com>, (C) 2007
//
// Copyright: See COPYING file that comes with this distribution
////////////////////////////////////////////////////////////////////////////////
#include "mtmlogger.h"
#include <QSettings>
#include <QTimer>
#include <qextserialport.h>
#include <QFile>
#include <QDataStream>
#include <QByteArray>
#include <iostream>
#include <QtDebug>

///////////////////Changes, last above//////////////////////
// added Ctrl+C termination handling - writing endfing tag in data file
//-----------
// added cleanUp() to distructor
////////////////////////////////////////////////////////////

#define MIN_BUFF     500    //minimum size of buffered data to read on serial 
///////////////Global conviniance conversion functions////////////////////////
BaudRateType intToBaud(int baud)
{
    switch(baud)
    {
        case 50:        return BAUD50;
        case 75:        return BAUD75;
        case 110:       return BAUD110;
        case 134:       return BAUD134;
        case 150:       return BAUD150;
        case 200:       return BAUD200;
        case 300:       return BAUD300;
        case 600:       return BAUD600;
        case 1200:      return BAUD1200;
        case 1800:      return BAUD1800;
        case 2400:      return BAUD2400;
        case 4800:      return BAUD4800;
        case 9600:      return BAUD9600;
        case 19200:     return BAUD19200;
        case 38400:     return BAUD38400;
        case 56000:     return BAUD56000;
        case 57600:     return BAUD57600;
        case 76800:     return BAUD76800;
        case 115200:    return BAUD115200;
        case 128000:    return BAUD128000;
        case 256000:    return BAUD256000;
        default:        return BAUD9600;
    }
    
}

DataBitsType intToDataBits(int bits)
{
    switch(bits)
    {
        case 5: return DATA_5;
        case 6: return DATA_6;
        case 7: return DATA_7;
        case 8: return DATA_8;
        default: return DATA_8;
    }
}

ParityType   strToParity(QString parity)
{
    if(parity.toLower() == "none" ) return PAR_NONE;
    if(parity.toLower() == "even" ) return PAR_EVEN;
    if(parity.toLower() == "mark" ) return PAR_MARK;
    if(parity.toLower() == "space") return PAR_SPACE;
    if(parity.toLower() == "odd" ) return PAR_ODD;
    return PAR_NONE; //default
}

StopBitsType floatToStopBits(float bits)
{
    switch((int)bits)
    {
        case 1: return STOP_1;
        case 2: return STOP_2;
    }
    
    if(bits < 2.0) return STOP_1_5;
    
    return STOP_1; //default
}

FlowType     strToFlow(QString flow)
{
    if(flow.toLower() == "off") return FLOW_OFF;
    if(flow.toLower() == "hardware") return FLOW_HARDWARE;
    if(flow.toLower() == "xonxoff" || flow.toLower() == "software" ) return FLOW_XONXOFF;
    return FLOW_OFF; //default
}
//////////////////////////////////////////////////////////////////////
MTMLogger::MTMLogger(QObject *parent)
 : QObject(parent),
m_serial(NULL),
m_outStream(NULL),
m_file(NULL),
m_pollTimer(NULL)      
{
    initSerial();
    if(m_strModule.isEmpty()) 
        m_strModule = "tetelemetry";
    m_file = new QFile(m_strModule+".dat"); 
    if (!m_file->open(QIODevice::WriteOnly | QIODevice::Truncate))
    {
        std::cout<<"could not open telemetry data file for writing:"<<std::endl;
        return;
    }
    m_outStream = new QDataStream(m_file);
    m_pollTimer = new QTimer(this);
    m_pollTimer->setInterval(300);
    connect(m_pollTimer,SIGNAL(timeout()),this,SLOT(pollSerial()));
    //connect(m_serial,SIGNAL(readyRead()),this,SLOT(pollSerial()));
    m_pollTimer->start();
    
}


MTMLogger::~MTMLogger()
{
  cleanUp();
  if(m_file) delete m_file;
  if(m_outStream) delete m_outStream;
}


void MTMLogger::cleanUp()
{
  if(m_file)
  {
    std::cout<<"closing data file..."<<std::endl;
    if(m_file->isOpen())
    {
      const int bytes = m_serial->bytesAvailable();
      
      //read the rest of data that is still in the buffer
      if(bytes)
      {
        char data[bytes];
        m_serial->readData(data,bytes);
        if(m_outStream) 
        {
            m_outStream->writeRawData(data,bytes);
            std::cout<<"Pending data was read from port and saved."<<std::endl;
        }
        else {
          std::cout<<"m_outStream not initialized - could not write to file."<<std::endl;
        }
      }
      
     if(m_outStream) 
     {
      //*m_outStream<<"FILE_EOF";
       char eof[] = {'F','I','L','E','_','E','O','F','\0'};
       m_outStream->writeRawData(eof,9);
     }
     else {
       std::cout<<"m_outStream not initialized - could not write to file."<<std::endl;
     }
     m_file->flush();
     m_file->close();
     std::cout<<"Data file closed and signed with 'FILE_EOF'. \nEnding gracefully..."<<std::endl;
     return;
    }
    else std::cout<<"Data file was not open... aborting."<<std::endl;
  }
}

/*!
    \fn MTMLogger::pollSerial()
 */
void MTMLogger::pollSerial()
{
    m_pollTimer->stop();
    //m_data = NULL;
    const int bytes = m_serial->bytesAvailable();
    char data[bytes];
    int bytesWritten = 0;
    if(bytes >= MIN_BUFF)
    {
        m_serial->readData(data,bytes);
        bytesWritten = m_outStream->writeRawData(data,bytes);
        //if(m_bStdOutput)
        //    printf("%d - ",data[0]);//std::cout<<data<<'\0'<<"-";
        if(bytes != bytesWritten )
            std::cout<<"not all bytes written to disk, lost "<<bytes - bytesWritten<<" bytes."<<std::endl;
    }
    m_pollTimer->start();
}


/*!
    \fn MTMLogger::initSerial()
 */
void MTMLogger::initSerial()
{
    QSettings settings("settings.conf",QSettings::IniFormat);
    m_strModule   = settings.value("Serial_Port/module").toString(); 
    m_bStdOutput  = settings.value("Serial_Port/stdoutput").toBool(); 
    m_strPort     = settings.value("Serial_Port/port").toString();
    m_iSpeed    = settings.value("Serial_Port/speed").toInt();
    m_iDataBits = settings.value("Serial_Port/dataBits").toInt();
    float fStopBits = settings.value("Serial_Port/stopBits").toString().toFloat();
    QString strParity   = settings.value("Serial_Port/parity").toString();
    m_strFlow     = settings.value("Serial_Port/flow").toString();
    
    //TODO: change serial initialization to take params from conf file
    if(!m_serial)
        m_serial = new QextSerialPort(m_strPort);
    if(!m_serial) std::cout<<m_strModule.toStdString()<<"TM logger could not allocate serial port object"<<std::endl;
    else {
        //TODO: get these values from the conf file (make a global function to be used by other modules as well)
        
        m_serial->setBaudRate(intToBaud(m_iSpeed));
        m_serial->setDataBits(intToDataBits(m_iDataBits));
        m_serial->setParity(strToParity(strParity));
        m_serial->setStopBits(floatToStopBits(fStopBits));
        m_serial->setFlowControl(strToFlow(m_strFlow));
//      m_serial->setName(port);
        if(!m_serial->open(QIODevice::ReadWrite|QIODevice::Unbuffered))
            std::cout<<m_strModule.toStdString()<<" TM logger could not open serial port "<<m_strPort.toStdString()<<std::endl;
        else std::cout<<m_strModule.toStdString()<<" TM logger opened "<<m_strPort.toStdString()<<std::endl;
    }

}
